// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: daniel

#include "coin2/exchange/gateio/api_util/auth_key.h"

#include <fmt/format.h>
#include <nlohmann/json.hpp>

#include "coin2/base/time.h"
#include "coin2/exchange/base/api_util/api_signature.h"

auto GateioBaseAuth::GetHeaderSet(
    std::string_view method,
    std::string_view target,
    std::string_view query_str,
    std::string_view payload) -> const HeaderSet& {
  CHECK_THROW(!header_set_.empty()) << "Header set is empty";
  int64_t timestamp = GetCurrentTimestamp() / 1e9;
  auto hashed_payload = ApiSignatureUtil::GetHashByHexSHA512(payload.data());
  std::string raw_str =
      fmt::format("{}\n{}\n{}\n{}\n{}", method, target, query_str, hashed_payload, timestamp);
  header_set_[0].second = access_key_;
  header_set_[1].second = fmt::format("{}", timestamp);
  header_set_[2].second = ApiSignatureUtil::GetSignatureByHexHmacSHA512(raw_str, secret_key_);
  return header_set_;
}

AsioHttpContext* GateioRestAuth::CreateRequestByGetWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  auto ahc = AsioHttpContext::NewGet(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet("GET", address, query_string, ""));
  ahc->keep_user_agent = true;

  return ahc;
}

AsioHttpContext* GateioRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = builder->ComputePostBody();
  return CreateRequestByPostWithSignature(address, query_string, post_body);
}

AsioHttpContext* GateioRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    const std::string& query_string,
    const std::string& post_body) {
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  auto ahc = AsioHttpContext::NewPost(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet("POST", address, query_string, post_body),
      post_body);
  ahc->keep_user_agent = true;

  return ahc;
}

AsioHttpContext* GateioRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    const std::string& post_body) {
  return CreateRequestByPostWithSignature(address, "", post_body);
}

AsioHttpContext* GateioRestAuth::CreateRequestByDeleteWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = builder->ComputePostBody();
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }

  return AsioHttpContext::NewDelete(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet("DELETE", address, query_string, ""),
      "");
}

const std::string GateioWsAuth::CreateSignature() const {
  int64_t time = GetCurrentTimestamp() / 1e6;
  std::string time_str = fmt::format("{}", time);
  std::string sign = ApiSignatureUtil::GetSignatureByHexHmacSHA512(time_str, key_.secret_key());
  std::string params = "\"" + key_.access_key() + "\",\"" + sign + "\"," + time_str;
  return fmt::format(R"({{"id": 10000, "method": "server.sign", "params": [{}]}})", params);
}

std::string GateioWsAuth::CreateSignature(
    const std::string& channel,
    const std::string& event,
    int64_t time) const {
  const std::string secret_key = key_.secret_key();
  const std::string api_key = key_.access_key();
  if (secret_key.empty() || api_key.empty()) {
    throw std::runtime_error("[Gateio] API key and secret key are required");
  }
  std::string cred = fmt::format("channel={}&event={}&time={}", channel, event, time);
  std::string sign = ApiSignatureUtil::GetSignatureByHexHmacSHA512(cred, secret_key);
  return fmt::format(R"({{"method": "api_key", "KEY": "{}", "SIGN": "{}"}})", api_key, sign);
}
